package org.changs.aplug;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.view.View;
import android.widget.TextView;

import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.gsonparserfactory.GsonParserFactory;

import org.changs.aplug.base.ActivityDelegate;
import org.changs.aplug.base.BaseActivity;
import org.changs.aplug.base.BaseFragment;
import org.changs.aplug.base.IDelegateOpt;
import org.changs.aplug.interf.OnApplicationListener;
import org.changs.aplug.plug.AppEventButInject;
import org.changs.aplug.utils.CrashUtils;
import org.changs.aplug.utils.JsonUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import io.reactivex.annotations.NonNull;
import io.reactivex.exceptions.UndeliverableException;
import io.reactivex.functions.Consumer;
import io.reactivex.plugins.RxJavaPlugins;
import timber.log.Timber;

/**
 * Created by yincs on 2017/7/25.
 */

public class AplugManager {
    private static AplugManager sAplugManager = new AplugManager();

    public static AplugManager get() {
        return sAplugManager;
    }

    private boolean debug;
    private List<OnApplicationListener> onApplicationListeners;
    private Set<Application.ActivityLifecycleCallbacks> activityLifecycleListener;
    private Set<FragmentManager.FragmentLifecycleCallbacks> fragmentLifecycleListener;
    private ActivityDelegateOptCreator activityDelegateOptCreator;
    private Application application;
    private ActivityManager activityManager = ActivityManager.getInstance();

    public void onCreate(Config config) {
        this.application = config.application;
        this.debug = config.debug;
        this.onApplicationListeners = config.onApplicationListenerList;
        this.activityLifecycleListener = config.activityLifecycleListener;
        this.fragmentLifecycleListener = config.fragmentLifecycleListener;
        this.activityDelegateOptCreator = config.activityDelegateOptCreator;

        initNetwork();
        initRxJava();
        initTree();
        initCrash();

        this.activityLifecycleListener.add(new AppActivityLifecycleCallbacks());
        this.activityLifecycleListener.add(AppEventButInject.getInstance());
        this.fragmentLifecycleListener.add(new AppFragmentLifecycleCallbacks());
        this.fragmentLifecycleListener.add(AppEventButInject.getInstance());

        for (Application.ActivityLifecycleCallbacks activityLifecycleCallbacks : this.activityLifecycleListener) {
            this.application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
        }
        onCreate();
    }

    private void initNetwork() {
        AndroidNetworking.initialize(application);
        if (debug) AndroidNetworking.enableLogging();
        AndroidNetworking.setParserFactory(new GsonParserFactory(JsonUtils.getMapper()));
    }

    private void initCrash() {
        CrashUtils.getInstance().init(application);
    }

    private void initTree() {
        Timber.plant(debug ? new Timber.DebugTree() : new Timber.Tree() {
            @Override
            protected void log(int priority, String tag, String message, Throwable t) {
                //do nothing
            }
        });
    }

    private void initRxJava() {
        RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
            @Override
            public void accept(@NonNull Throwable throwable) throws Exception {
                Throwable actual;
                if (throwable instanceof UndeliverableException) {
                    actual = throwable.getCause();
                    Timber.e("UndeliverableException: ");
                    actual.printStackTrace();
                } else {
                    actual = throwable;
                }
                if (actual instanceof Exception)
                    throw (Exception) actual;
                throw new Exception(actual);
            }
        });
    }

    public boolean isDebug() {
        return debug;
    }

    public Context getContext() {
        return application;
    }

    private void onCreate() {
        for (OnApplicationListener onApplicationListener : onApplicationListeners) {
            onApplicationListener.onCreate(application);
        }
    }

    public void onTerminate() {
        for (OnApplicationListener onApplicationListener : onApplicationListeners) {
            onApplicationListener.onTerminate();
        }
    }

    public IDelegateOpt creatorActivityDelegateOpt(BaseActivity activity) {
        if (activityDelegateOptCreator != null) return activityDelegateOptCreator.create(activity);
        return new ActivityDelegate(activity);
    }

    public static class Config {
        final Application application;
        List<OnApplicationListener> onApplicationListenerList = new ArrayList<>();
        Set<Application.ActivityLifecycleCallbacks> activityLifecycleListener = new HashSet<>();
        Set<FragmentManager.FragmentLifecycleCallbacks> fragmentLifecycleListener = new HashSet<>();
        ActivityDelegateOptCreator activityDelegateOptCreator;
        boolean debug;

        public Config(Application application) {
            this.application = application;
        }

        public Config debug() {
            debug = true;
            return this;
        }

        public Config debug(boolean debug) {
            this.debug = debug;
            return this;
        }

        public Config addApplicationListener(OnApplicationListener onApplicationListener) {
            onApplicationListenerList.add(onApplicationListener);
            return this;
        }

        public Config addActivityLifecycleListener(Application.ActivityLifecycleCallbacks lifecycleListener) {
            activityLifecycleListener.add(lifecycleListener);
            return this;
        }

        public Config addFragmentLifecycleListener(FragmentManager.FragmentLifecycleCallbacks lifecycleListener) {
            fragmentLifecycleListener.add(lifecycleListener);
            return this;
        }

        public Config activityDelegateOptCreator(ActivityDelegateOptCreator activityDelegateOptCreator) {
            this.activityDelegateOptCreator = activityDelegateOptCreator;
            return this;
        }
    }

    public interface ActivityDelegateOptCreator {
        IDelegateOpt create(BaseActivity activity);
    }


    private final class AppActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            injectActivityManager(activity, true);
            injectFragmentLifecycleCallbacks(activity, true);
        }

        @Override
        public void onActivityStarted(Activity activity) {
        }

        @Override
        public void onActivityResumed(Activity activity) {
            injectActivityTitleAndBack(activity);
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
            injectFragmentLifecycleCallbacks(activity, false);
            injectActivityManager(activity, false);
        }

        private void injectFragmentLifecycleCallbacks(Activity activity, boolean register) {
            if (activity instanceof FragmentActivity) {
                final FragmentManager fragmentManager = ((FragmentActivity) activity).getSupportFragmentManager();
                for (FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks : fragmentLifecycleListener) {
                    if (register) {
                        fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
                    } else {
                        fragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks);
                    }
                }
            }
        }

        private void injectActivityTitleAndBack(final Activity activity) {
            TextView textView = (TextView) activity.findViewById(R.id.base_tv_title);
            if (textView != null) textView.setText(activity.getTitle());
            View goBack = activity.findViewById(R.id.base_go_back);
            if (goBack != null) goBack.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    activity.onBackPressed();
                }
            });
        }

        private void injectActivityManager(Activity activity, boolean register) {
            if (register) {
                activityManager.onActivityCreated(activity);
            } else {
                activityManager.onActivityDestroyed(activity);
            }
        }

    }

    public class AppFragmentLifecycleCallbacks extends FragmentManager.FragmentLifecycleCallbacks {
        @Override
        public void onFragmentResumed(FragmentManager fm, Fragment f) {
            injectFragmentTitleAndBack(f);
        }

        private void injectFragmentTitleAndBack(Fragment fragment) {
            if (!(fragment instanceof BaseFragment)) return;
            final BaseFragment bFragment = (BaseFragment) fragment;

            TextView textView = bFragment.findViewById(R.id.base_tv_title);
            if (textView != null) textView.setText(bFragment.getTitle());
            View goBack = bFragment.findViewById(R.id.base_go_back);
            if (goBack != null) goBack.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    bFragment.getContext().onBackPressed();
                }
            });
        }
    }


}
